import java.util.HashSet;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: fzykd
 * Date: 2023-03-05
 * Time: 20:21
 */

@SuppressWarnings({"all"})
public class HashSetSource {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add("java");
        hashSet.add("php");
        hashSet.add("java");
        System.out.println("set=" + hashSet);

        //第一次add
        /*
        源码解读
        1.先执行的构造器 HashMap
            public HashSet() {
                map = new HashMap<>();
            }

        2.执行add方法 //e 已经是字符串常量 ”java“
            public boolean add(E e) {
                return map.put(e, PRESENT)==null; (static) PRESENT = new Object();
            }

        3.执行了put()方法 key 是 java  value 是 PRESENT
             public V put(K key, V value) {
                return putVal(hash(key), key, value, false, true);
            } 该方法会执行 hash(key) 得到key对应的hash值 算法 h = key.hashCode()) ^ (h >>> 16) 哈希值 不是hashCode

         4. 执行putVal
          final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
                Node<K,V>[] tab; Node<K,V> p; int n, i; //定义了辅助遍历
                if ((tab = table) == null || (n = tab.length) == 0)
                //table就是存放Node节点的数组
                //表示当前的table是空的 第一次扩容 大小16
                    n = (tab = resize()).length;
                if ((p = tab[i = (n - 1) & hash]) == null)
                    tab[i] = newNode(hash, key, value, null);
                //根据key,得到hash 去计算该key应该存放到table表的哪个索引位置
                //并且把这个位置的对象 赋值给p
                //判断p是否为空 如果为空 表示没有任何数据 就创建一个Node(key,value)
                //放在该位置
                else {
                //开发技巧:定义遍历的时候 在需要局部遍历的时候在创建
                //这个else是第三次add 添加了相同的元素的java之后进来的判断 三种清空
                    Node<K,V> e; K k;
                    if (p.hash == hash &&  //如果当前索引位置的链表第一个元素和准备添加的hash值一样
                        ((k = p.key) == key || (key != null && key.equals(k))))
                        //并且满足 以上两个条件之一是
                        //准备加入的key 和 p指向的Node节点key是用一个对象
                        //p 指向的Node节点的 key 的equals() 和准备加入的key比较相同
                        //就不能加入
                        e = p;
                        //在判断 p是不是红黑树 在按照红黑树的条件比较
                        //如果是 调用putTreeVal 方法来进行添加
                    else if (p instanceof TreeNode)
                        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                    else {
                    //for 链表 循环比较 有和一样的直接结束 没有相同的 添加到最后节点上
                    //元素添加到链表之后 马上判断是否到了8个节点 对当前链表进行数化
                    //在转成红黑树时 还要进行判断 数组大小是否大于等于64 那么就树化 不成立就先扩容
                        for (int binCount = 0; ; ++binCount) {
                            if ((e = p.next) == null) {
                                p.next = newNode(hash, key, value, null);
                                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                    treeifyBin(tab, hash);
                                break;
                            }
                            if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                                break;
                            p = e;
                        }
                    }
                    if (e != null) { // existing mapping for key
                        V oldValue = e.value;
                        if (!onlyIfAbsent || oldValue == null)
                            e.value = value;
                        afterNodeAccess(e);
                        return oldValue;
                    }
                }
                ++modCount;
                //放了数据之后 判断 大于12之后 那么就扩容
                if (++size > threshold)
                    resize();
                afterNodeInsertion(evict);
                return null;
            }

         */



        /*
        2.第二次add
         */

    }
}
